
package roleadminservices;

import java.io.*;
import java.sql.*;
import java.text.SimpleDateFormat;

import javax.xml.rpc.Call;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.ParameterMode;
import java.util.*;

public class RoleAdminImpl implements RoleAdminIF {
    

	/* (non-Javadoc)
	 * @see roleadminservices.RoleAdminIF#grantRole(java.lang.String, java.lang.String, java.lang.String)
	 */
	public boolean grantRole(String roleName, String EPCClass, String comId)
	{
	
		//define the driver to use 
	      String driver = "org.apache.derby.jdbc.ClientDriver";
	    //the database name  
	      String dbName="RSSdb";
	    //define the Derby connection URL to use 
	      String connectionURL = "jdbc:derby://localhost:1527/" + dbName;
	    //define the Display string
	      String disp="\nInvoking RSS's grantRole Service... \n";
	    //define Simple Majority Voting result displayer
	      String dispSMV="";
	      
	      Connection conn = null;
	      Statement s;
	      Statement s1;
	      Statement s2;
	      ResultSet rs;
	      ResultSet rs1;
	      
	    //define a SQL Date variable

	    //the number of partners
	      int partners=0;
	    //the number of positive votes
	      int positive=0;
	      

	      
	    //load derby driver
	      try{
	          Class.forName(driver); 
//	          System.out.println(driver + " loaded. ");
	          
	      } catch(java.lang.ClassNotFoundException e)     {
	          System.err.print("ClassNotFoundException: ");
	          System.err.println(e.getMessage());
	          System.out.println("\n    >>> Please check your CLASSPATH variable   <<<\n");
	      }
	      
		//look for partners
		try{

			conn = DriverManager.getConnection(connectionURL);
//			System.out.println("Connected to database " + dbName);
			disp+=("Connect to RSS's database " + dbName+" to look for vote partners... \n");
			System.out.println(disp);
			
			s=conn.createStatement();
			s1=conn.createStatement();
			
// verify the target role with epc has not been assigned to the company
			//construct sql: search role grant information for the request
			String sqlRoleGrant = "SELECT * FROM roleGrant WHERE lid="+comId+" AND EPCClass='"+EPCClass+"' AND roleName='"+roleName+"'";
			rs = s.executeQuery(sqlRoleGrant);
			if(rs.next()){
				disp+="Role has been granted!";
				return false;
			}
			
			//construct sql: get all non-repeat partners' id 
			String sqlRoleName = "SELECT * FROM roleDictionary WHERE roleName='"+roleName+"'";
			rs1 = s.executeQuery(sqlRoleName);
			
			//construct sql: get all non-repeat partners' id 
			String sqlPartnerId = "SELECT lid_to FROM itemTrack WHERE EPC LIKE '"+EPCClass+"%' GROUP BY lid_to";
			
			rs = s.executeQuery(sqlPartnerId);
			

			while(rs.next()){
				partners++;

			//get partners' id
				String partId = rs.getString("lid_to");

			//retrieve parter's address according to the id
				String sqlAddr = "SELECT address FROM company where lid="+partId;
				ResultSet rsAddr = s1.executeQuery(sqlAddr);
				
			 
			//there is a unique partnerAddr to a partId
				String partnerAddr=null;
				rsAddr.next();
				partnerAddr = rsAddr.getString("address");

				if(partnerAddr==null){
//					System.out.println("\n Internal DB error occurs! ");
					disp+=("\n Internal DB error occurs! ");
					System.out.println(disp);
					return false;

				}
					
			//use DII to invoke partners voting services
				boolean bResult=false;
				System.out.println("Invoking partner: "+partnerAddr+"'s Voting Service...\n");
				bResult= invokeVoteService(partnerAddr, roleName, EPCClass, comId, partId);

				if (bResult==true)
					positive++;
				
			}
			//close connection
			conn.close();
		}catch(Exception e){
			e.printStackTrace();	
		}
		
		//check whether there exist partners
		if(partners==0)
		{
			System.out.println("There is no partners to vote for Role Grant.");			
			return false;
		}
		
		//make decision based on received voting results
		if(positive>partners/2){
//			System.out.println("\n"+positive+"of "+partners+"voted for granting, Simple Majority Voting fulfills! Role granted.");
			dispSMV+=("\n"+positive+"of "+partners+" voted for granting, Simple Majority Voting fulfills! Role will be granted.\n");			

//			System.out.println("Trying to insert role grant record into roleGrant table...");
			dispSMV+=("Trying to insert role grant record into roleGrant table...");
			
		    //get current date
			Calendar now = Calendar.getInstance();
			//set date format compatible to SQL date format
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
			//add one year in the Year field of now
			now.set(Calendar.YEAR, now.get(Calendar.YEAR)+1);
			//format the Date
			String yearLater = sdf.format(now.getTime());

		    //construct the sql string to insert a record into roleGrant table in RSSdb, expire date is one year after
			String sqlInsert = "INSERT INTO roleGrant values("+comId+", '"+EPCClass+"','"+roleName+"','"+yearLater+"')";
			
			try{
				conn = DriverManager.getConnection(connectionURL);
				s2=conn.createStatement();
				s2.execute(sqlInsert);
//				System.out.println("Record insert succeed.");
				dispSMV+=("Record insert succeed. \nRole has been granted!");
				conn.close();
			}catch(SQLException e){
				e.printStackTrace();
			}
			
			System.out.println(dispSMV);
			return true;

		}else{
//			System.out.println("\n"+positive+"of "+partners+"voted for granting, Simple Majority Voting does not fulfills! Role is not granted.");	
			dispSMV+=("\n"+positive+"of "+partners+" voted for granting, Simple Majority Voting does not fulfill! Role is not granted.");
			System.out.println(dispSMV);
			return false;
		}
    }
	
	//define DII method
	private static String qnameService = "VoteService";
    private static String qnamePort = "VoteIF";
    private static String BODY_NAMESPACE_VALUE = "urn:Foo";
    private static String ENCODING_STYLE_PROPERTY = "javax.xml.rpc.encodingstyle.namespace.uri";
    private static String NS_XSD = "http://www.w3.org/2001/XMLSchema";
    private static String URI_ENCODING = "http://schemas.xmlsoap.org/soap/encoding/";
    
    //voting result
    boolean result;

	/**
	 * A method constructing DII 
	 * @param addr					Address of partner company that provides vote service.
	 * @param roleName				Name of the target role.
	 * @param EPCClass				EPCClass value associated with the target role.
	 * @param comId					Applicant company's id.
	 * @param serviceProviderComId	Id of voting partner company.
	 * @return						true if voting partner supports for role grant.
	 */
	public boolean invokeVoteService(String addr, String roleName, String EPCClass, String comId, String serviceProviderComId){
				//construct complete endPointAddress
		   		String endPointAddr = addr+"Vote/vote";
		   		//define the display string
		   		String disp=null;
		   		
//		        System.out.println("Endpoint address = " + endPointAddr);
//		   		disp+=("Endpoint address = " + endPointAddr);
		   		
		   		

		        try {
		        	//create call
		            ServiceFactory factory = ServiceFactory.newInstance();
		            Service service = factory.createService(new QName(qnameService));
		            Call call = service.createCall(new QName(qnamePort));

		            call.setTargetEndpointAddress(endPointAddr);
		            call.setProperty(Call.SOAPACTION_USE_PROPERTY, new Boolean(true));
		            call.setProperty(Call.SOAPACTION_URI_PROPERTY, "");
		            call.setProperty(ENCODING_STYLE_PROPERTY, URI_ENCODING);

		            
		            QName QNAME_TYPE_STRING = new QName(NS_XSD, "string");
		            QName QNAME_TYPE_BOOLEAN = new QName(NS_XSD, "boolean");
		            //set return type
		            call.setReturnType(QNAME_TYPE_BOOLEAN);

//		            set access point method for service
		            call.setOperationName(new QName(BODY_NAMESPACE_VALUE, "vote"));
//		            //set parameters
		            call.addParameter("String_1", QNAME_TYPE_STRING, ParameterMode.IN);
		            call.addParameter("String_2", QNAME_TYPE_STRING, ParameterMode.IN);
		            call.addParameter("String_3", QNAME_TYPE_STRING, ParameterMode.IN);
		            call.addParameter("String_4", QNAME_TYPE_STRING, ParameterMode.IN);
		            
		            String[] params = {roleName, EPCClass, comId, serviceProviderComId};

		            //invoke the target method with constructed parameter(s)
//		            System.out.println("Accessing service on: "+endPointAddr+"...");
//		            disp+=("Accessing service on: "+endPointAddr+"...");
		            result = (Boolean) call.invoke(params);
//		            System.out.println("Voting result from "+endPointAddr+"is "+result);
//		            disp+=("Voting result from "+endPointAddr+"is "+result);


		        } catch (Exception ex) {
		            ex.printStackTrace();
		        }
//		        System.out.println(disp);
		        return result;
		    }
		
	
}
